package com.citywithincity.db;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.pool.DruidDataSource;
import com.citywithincity.db.interfaces.IDataSourceProvider;
import com.citywithincity.utils.StringUtils;

public class DruidDataSourceProvider implements IDataSourceProvider {

	// 基本属性 url、user、password

	// 初始连接池大小、最小空闲连接数、最大活跃连接数
	private int initialSize = 1;
	private int minIdle = 10;
	private int maxActive = 150;

	// 配置获取连接等待超时的时间(10秒)
	private long maxWait = 10 * 1000;

	// 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒
	private long timeBetweenEvictionRunsMillis = DruidDataSource.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
	// 配置连接在池中最小生存的时间
	private long minEvictableIdleTimeMillis = DruidDataSource.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
	// 配置发生错误时多久重连
	private long timeBetweenConnectErrorMillis = DruidDataSource.DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS;

	/**
	 * hsqldb - "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS" Oracle -
	 * "select 1 from dual" DB2 - "select 1 from sysibm.sysdummy1" mysql -
	 * "select 1"
	 */
	private String validationQuery = "select 1 FROM DUAL";
	private boolean testWhileIdle = true;
	private boolean testOnBorrow = false;
	private boolean testOnReturn = false;

	// 是否打开连接泄露自动检测
	private boolean removeAbandoned = false;
	// 连接长时间没有使用，被认为发生泄露时长,5分钟(1分钟)
	private long removeAbandonedTimeoutMillis = 60 * 1000;
	// 发生泄露时是否需要输出 log，建议在开启连接泄露检测时开启，方便排错
	private boolean logAbandoned = true;

	// 是否缓存preparedStatement，即PSCache，对支持游标的数据库性能提升巨大，如 oracle、mysql 5.5 及以上版本
	// private boolean poolPreparedStatements = false; // oracle、mysql 5.5
	// 及以上版本建议为 true;

	// 只要maxPoolPreparedStatementPerConnectionSize>0,poolPreparedStatements就会被自动设定为true，使用oracle时可以设定此值。
	private int maxPoolPreparedStatementPerConnectionSize = -1;

	// 配置监控统计拦截的filters
	private String filters; // 监控统计："stat" 防SQL注入："wall" 组合使用： "stat,wall"
	private List<Filter> filterList;

	private DruidDataSource ds;
	private ConnectionDescription description;
	
	public DruidDataSourceProvider(ConnectionDescription cd) {
		
		//filters = "stat";
		
		ds = new DruidDataSource();
		description = cd;
		ds.setPoolPreparedStatements(true);
		ds.setUrl(cd.getJdbcUrl());
		ds.setUsername(cd.getUser());
		ds.setPassword(cd.getPassword());
		ds.setDriverClassName(cd.getDriverClass());
		ds.setInitialSize(initialSize);
		ds.setMinIdle(minIdle);
		ds.setMaxActive(maxActive);
		ds.setMaxWait(maxWait);
		ds.setTimeBetweenConnectErrorMillis(timeBetweenConnectErrorMillis);
		ds.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
		ds.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
		
		ds.setValidationQuery(validationQuery);
		ds.setTestWhileIdle(testWhileIdle);
		ds.setTestOnBorrow(testOnBorrow);
		ds.setTestOnReturn(testOnReturn);
		
		ds.setRemoveAbandoned(removeAbandoned);
		ds.setRemoveAbandonedTimeoutMillis(removeAbandonedTimeoutMillis);
		ds.setLogAbandoned(logAbandoned);
		
		//只要maxPoolPreparedStatementPerConnectionSize>0,poolPreparedStatements就会被自动设定为true，参照druid的源码
		ds.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
		
		if (!StringUtils.isEmpty(filters))
			try {ds.setFilters(filters);} catch (SQLException e) {throw new RuntimeException(e);}
		
		addFilterList(ds);
	}
	private void addFilterList(DruidDataSource ds) {
		if (filterList != null) {
			List<Filter> targetList = ds.getProxyFilters();
			for (Filter add : filterList) {
				boolean found = false;
				for (Filter target : targetList) {
					if (add.getClass().equals(target.getClass())) {
						found = true;
						break;
					}
				}
				if (! found)
					targetList.add(add);
			}
		}
	}
	
	public DataSource getDataSource() {
		return ds;
	}
	
	


	
	public ArFactory getArFactory() throws SQLException {
		return description.create(this);
	}

	
	public void close() {
		if (ds != null)
			ds.close();
	}
	
	
	
	@Override
	public ArFactory createArFactory() throws SQLException {
		return description.create(this);
	}
	

	private static final Log logger = LogFactory.getLog(DruidDataSourceProvider.class);
	
	@Override
	public void close(Connection connection) {
		try {
			connection.close();
		} catch (Throwable e) {
			logger.fatal("释放连接",e);
		}
	}
	@Override
	public Connection getConnection() throws SQLException {
		
		return ds.getConnection();
	}

}
